package com.inyourcode.example.service.login.http;

import com.alibaba.fastjson.JSONObject;
import com.inyourcode.core.cluster.ClusterNodeConf;
import com.inyourcode.core.cluster.ClusterNodeManager;
import com.inyourcode.core.cluster.ClusterType;
import com.inyourcode.core.cluster.api.ClusterConst;
import com.inyourcode.core.util.HttpMetehodCaller;
import com.inyourcode.core.util.JWTUtil;
import com.inyourcode.core.db.redis.RedisConfig;
import com.inyourcode.core.transport.netty.handler.acceptor.DefaultHttpRequestHandler;
import com.inyourcode.example.service.IErrorCode;
import com.inyourcode.example.service.RedisScripter;
import com.inyourcode.core.transport.api.HttpAction;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.Map;

@Component
public class LoginHttpService implements InitializingBean {


    ValueOperations redisValueOperations;
    @Autowired
    DefaultHttpRequestHandler httpRequestHandler;
    @Autowired
    JWTUtil jwtUtil;
    @Autowired
    ClusterNodeManager clusterNodeManager;
    @Qualifier(RedisConfig.STRING_REDIS_TEMPLATE)
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    RedisScripter redisScripter;

    @PostConstruct
    void init() {
        redisValueOperations = redisTemplate.opsForValue();
    }

    @HttpAction(action = "login")
    public Object login(Map<String, String> param) {
        String openId = param.get("openId");
        HttpLoginResult result = new HttpLoginResult();
        result.code = 0;

        String clusterBindKey = ClusterConst.KEY_PLAYER_BIND_SERVER + openId;
        Object nodeUIdObj = redisValueOperations.get(clusterBindKey);

        ClusterNodeConf selectedNode;
        if (nodeUIdObj == null) {
            selectedNode = selectNewNode("", clusterBindKey);
        } else {
            String nodeUId = nodeUIdObj.toString();
            selectedNode = clusterNodeManager.getClusterNode(nodeUId);
            //老节点已经宕机
            if (selectedNode == null) {
                selectedNode = selectNewNode(nodeUId, clusterBindKey);
            }
        }

        if (selectedNode == null) {
            result.code = IErrorCode.LOGIN_NODE_NOT_FOUND;
            return result;
        }

        String playerIdKey = ClusterConst.KEY_PLAYER_ID_MAPPING + openId;
        Object playerIdObj = redisValueOperations.get(playerIdKey);
        if (playerIdObj == null) {
            playerIdObj = redisScripter.idGen(ClusterConst.KEY_PLAYER_ID_GEN);
            if (playerIdObj == null) {
                result.code = IErrorCode.LOGIN_PLAYER_DATA_GEN_ERROR;
                return result;
            }

            Object casPlayerIdObj = redisScripter.cas(playerIdKey, "", playerIdObj.toString());
            if (casPlayerIdObj == null) {
                result.code = IErrorCode.LOGIN_PLAYER_DATA_GEN_ERROR;
                return result;
            }

            if (!Boolean.valueOf(casPlayerIdObj.toString())) {
                result.code = IErrorCode.LOGIN_PLAYER_DATA_GEN_ERROR;
                return result;
            }

        }

        JSONObject data = new JSONObject();
        data.put("playerId", playerIdObj);
        String jwtStr = jwtUtil.create(openId, data.toJSONString());

        String clientAddr = selectedNode.getExtend().get(ClusterConst.KEY_CLUSTER_EXTEND_ADDR);
        String[] split = clientAddr.split(":");
        String host = split[0];
        int port = Integer.valueOf(split[1]);

        result.host = host;
        result.port = port;
        result.token = jwtStr;

        return result;
    }

    private ClusterNodeConf selectNewNode(String old, String clusterBindKey) {
        ClusterNodeConf lowNode = clusterNodeManager.selectLowNode(ClusterType.LOBBY);
        if (lowNode == null) {
            return null;
        }

        String nodeUId = lowNode.uniqueKey();
        if (!lowNode.isActive()) {
            return null;
        }

        Object execute = redisScripter.cas(clusterBindKey, old, nodeUId);
        if (execute == null) {
            return null;
        }

        if (!Boolean.valueOf(execute.toString())) {
            return null;
        }

        return lowNode;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Method[] declaredMethods = LoginHttpService.class.getDeclaredMethods();
        for (Method method : declaredMethods) {
            if (!method.isAnnotationPresent(HttpAction.class)) {
                continue;
            }
            HttpAction annotation = method.getAnnotation(HttpAction.class);
            HttpMetehodCaller httpMetehodCaller = new HttpMetehodCaller();
            httpMetehodCaller.setAction(annotation.action());
            httpMetehodCaller.setMethod(method);
            httpMetehodCaller.setObject(this);
            this.httpRequestHandler.register(httpMetehodCaller);
        }
    }

    static class SelectNodeResult {
        ClusterNodeConf nodeConf;
    }
}
